Guide to App Architecutre | Domain Layer
一言で表すと
Domain
概要
The domain layer is an optional layer that sits between the UI layer and the data layer.
domain layer は UI と data をつなぐ optional な layer である
Mori Atsushi.icon 僕は基本入れたほうが良いと思います
domain layer でやること
複数の viewmodel で再利用されるビジネスロジックのカプセル化
複雑なビジネスロジックを置く場所
複雑性や複数からの利用をまとめる場所であるので、optional な layer といえる。
domain layer を導入するメリット
コード重複を避けることができる
Mori Atsushi.icon コードの重複除去の目的で入れると失敗しそうなイメージ
domain layer のクラスを使用するクラス (つまり viewmodel とか) の可読性を向上させる
testability の向上
などなど
domain layer で守ったほうがいいこと
各ユースケースは単一責任の原則を守るべき
mutable なデータを持つべきでない (immutable であるべき)
UI layer や data layer などで mutable data を handle するべき
App Architecture Guide 内での命名規則
verb in present tense + noun/what (optional) + UseCase.
(現在形の動詞 + 名詞 + UseCase)
example
FormatDateUseCase
LogOutUserUseCase
GetLatestNewsWithAuthorsUseCase
MakeLoginRequestUseCase
chigichan24.icon これは結構守ったほうがいいと思った。
依存関係
一般的には data layer の repository に依存する。
code: kotlin
class GetLatestNewsWithAuthorsUseCase(
private val newsRepository: NewsRepository,
private val authorsRepository: AuthorsRepository
) { /* ... */ }
https://scrapbox.io/files/61b9ded20b645d001da51537.png
UI layer には callback や coroutines などでやりとりをする。
Call Use case in Kotlin
Kotlinでは、operator修飾子を付けてinvoke()関数を定義することで、UseCase Class Instanceを関数として呼び出せるようにすることができる。
rmakiyama.icon好きでいつもやってる
Mori Atsushi.icon 最近suspend functionもできるようになったはず
code:kotlin
class FormatDateUseCase(userRepository: UserRepository) {
private val formatter = SimpleDateFormat(
userRepository.getPreferredDateFormat(),
userRepository.getPreferredLocale()
)
operator fun invoke(date: Date): String {
return formatter.format(date)
}
}
code:kotlin
class MyViewModel(formatDateUseCase: FormatDateUseCase) : ViewModel() {
init {
val today = Calendar.getInstance()
val todaysDate = formatDateUseCase(today)
/* ... */
}
}
(感想) 結構細かい単位で UseCase 切るならこういう感じでやっていくのもありだけど、そうすると、ViewModel で鬼ほど UseCase の dependency を inject するはめになりそう
LifeCycle
特定の lifecycle に依存するべきでないし、独自のライフサイクルを持つべきでない。
依存関係で注入するたびにインスタンス化するべき
Threading
メインスレッドから呼び出しても安全なようにするべき
長時間スレッドをブロックするような処理ならば、適切なスレッドに移動させる責務がある。
ただし、本当に別スレッドに送って処理をしていいのかはちゃんと考えよう。
一般に、複雑な計算はキャッシュさせるべきで、そういうのは data layer で処理させて、結果を保持したりするほうがうまくいくことが多い。
その他 tips
Reusable simple business logic
UIレイヤーに存在する繰り返し可能なビジネスロジックは、ユースケースクラスにカプセル化したほうがいい。
こうすることで、そのロジックが使用されるすべての場所で、簡単に変更を適用することができる。
また、そのロジックを単独でテストすることもできる。
こういうとき、Util Class とかに追いやって、static な関数にしがちだけど、これはしばしば良くない (note)
Combine repositories
複数の repository のデータを結合していい感じにできる。
ViewModel からロジックを抽象化できる。
https://scrapbox.io/files/61b9e1de0924b100232052f0.png
ちなみに、Room で Relation を持つようなデータを join して持ってきたいときは、UseCase ではなく Repository でその処理をやったほうがいい。
その他
うまく UseCase をつくると、Androidに限らず、WearやTVからもいい感じに参照できる。
Mori Atsushi.icon それが難しい
気になるポイント
コメント